
//
//  ES2Renderer.m
//  iKanKanTest
//
//  Created by dragon on 10/30/10.
//  Copyright Thunder Networking Inc 2010. All rights reserved.
//

#include "ekk_interface.h"
#import "ES2Renderer.h"

#if 0
#import <pthread.h>
#define LOG_FUNC_CALL()  NSLog(@"[TVOUT %d]CALL %s :%d",self,__FUNCTION__,__LINE__);
#define LOGDB NSLog
#else
#define LOG_FUNC_CALL(...) 
#define LOGDB(...) 
#endif


//////////////////////YUV FUNCTIONS//////////////
#define TEST_SAVE_FILE 0
#if TEST_SAVE_FILE
#include <stdio.h>
#define TEST_FILE_PATH "/ffffuck.yuv"
#endif

#define TEST_MULTI_THREAD 0

#if 1
#define YUV_VERTEX_SCRIPT\
	"attribute vec4 vPosition;\n"\
	"attribute vec2 vTexCoord;\n"\
	"attribute vec2 vLogoTexCoord;\n"\
	"attribute vec2 vSubtitleTexCoord;\n"\
	"varying mediump vec2 texCoord;\n"\
	"varying mediump vec2 logoTexCoord;\n"\
	"varying mediump vec2 subtitleTexCoord;\n"\
	"void main()\n"\
	"{\n"\
	"	texCoord = vTexCoord;\n"\
	"	logoTexCoord = vLogoTexCoord;\n"\
	"	subtitleTexCoord = vSubtitleTexCoord;\n"\
	"	gl_Position = vPosition;\n"\
	"}\n"\

#define YUV_TEXTURE_SCRIPT\
"varying mediump vec2 texCoord;\n"\
"varying mediump vec2 logoTexCoord;\n"\
"varying mediump vec2 subtitleTexCoord;\n"\
"uniform mediump vec2 yCoord;\n"\
"uniform mediump vec2 uCoord;\n"\
"uniform mediump vec2 vCoord;\n"\
"uniform mediump mat4 blendtest;\n"\
" mediump mat4 blendfactor_c;\n"\
"\n"\
"uniform sampler2D txture;\n"\
"uniform sampler2D logoTexture;\n"\
"uniform sampler2D subtitleTexture;\n"\
"uniform sampler2D txture_y;\n"\
"uniform sampler2D txture_u;\n"\
"uniform sampler2D txture_v;\n"\
"uniform int use_yuv;\n"\
"lowp float y,u,v;\n"\
"lowp vec2 sub_coord;\n"\
"lowp vec3 tmp_yuv;\n"\
"const lowp mat3 cvrt_mtx = mat3(vec3(1.164,1.164,1.164),vec3(0.0,-0.391,2.018),vec3(1.596,-0.813,0.0));\n"\
"const lowp vec3 diff = vec3(0.0625,0.5,0.5);\n"\
"varying highp float idx;\n"\
"int cnt;\n"\
"lowp float tmp_y[4];\n"\
"lowp vec4 logo_color;\n"\
"lowp vec4 subtitle_color;\n"\
"lowp float correct;\n"\
"\n"\
"void main()\n"\
"{\n"\
"	//subtitle_color = texture2D(subtitleTexture,subtitleTexCoord);\n"\
"	tmp_yuv.x = texture2D(txture_y,texCoord).a;\n"\
"	tmp_yuv.y = texture2D(txture_u,texCoord).a;\n"\
"	tmp_yuv.z = texture2D(txture_v,texCoord).a;\n"\
"   correct = min(tmp_yuv.x*30.0,1.0);\n"\
"   //tmp_yuv.xyz = max(tmp_yuv.xyz,diff);\n"\
"   gl_FragColor.rgb =cvrt_mtx * (tmp_yuv - diff) ;\n"\
"	gl_FragColor.a = correct;\n"\
"	//gl_FragColor.rgb = vec3(1.0,0.0,0.0);\n"\
"}\n"\
""
#define YUV_TEXTURE_SCRIPT_SUBTITLE\
"varying mediump vec2 texCoord;\n"\
"varying mediump vec2 logoTexCoord;\n"\
"varying mediump vec2 subtitleTexCoord;\n"\
"uniform mediump vec2 yCoord;\n"\
"uniform mediump vec2 uCoord;\n"\
"uniform mediump vec2 vCoord;\n"\
"uniform mediump mat4 blendtest;\n"\
" mediump mat4 blendfactor_c;\n"\
"\n"\
"uniform sampler2D txture;\n"\
"uniform sampler2D logoTexture;\n"\
"uniform sampler2D subtitleTexture;\n"\
"uniform sampler2D txture_y;\n"\
"uniform sampler2D txture_u;\n"\
"uniform sampler2D txture_v;\n"\
"uniform int use_yuv;\n"\
"lowp float y,u,v;\n"\
"lowp vec2 sub_coord;\n"\
"lowp vec3 tmp_yuv;\n"\
"const lowp mat3 cvrt_mtx = mat3(vec3(1.164,1.164,1.164),vec3(0.0,-0.391,2.018),vec3(1.596,-0.813,0.0));\n"\
"const lowp vec3 diff = vec3(0.0625,0.5,0.5);\n"\
"varying highp float idx;\n"\
"int cnt;\n"\
"lowp float tmp_y[4];\n"\
"lowp vec4 logo_color;\n"\
"lowp vec4 subtitle_color;\n"\
"lowp float correct;\n"\
"\n"\
"void main()\n"\
"{\n"\
"   //blendfactor_c =blendtest;\n"\
"	subtitle_color = texture2D(subtitleTexture,subtitleTexCoord);\n"\
"	tmp_yuv.x = texture2D(txture_y,texCoord).a;\n"\
"	tmp_yuv.y = texture2D(txture_u,texCoord).a;\n"\
"	tmp_yuv.z = texture2D(txture_v,texCoord).a;\n"\
"   tmp_yuv.xyz = max(tmp_yuv.xyz,diff);\n"\
"   gl_FragColor.rgb =cvrt_mtx * (tmp_yuv - diff) ;\n"\
"	gl_FragColor.rgb = mix(gl_FragColor.rgb, subtitle_color.rgb, subtitle_color.a);\n"\
"	gl_FragColor.a = 1.0;\n"\
"   //gl_FragColor *=blendfactor_c;\n"\
"	//gl_FragColor.rgb = vec3(1.0,0.0,0.0);\n"\
"}\n"\
""
#define YUV_TEXTURE_SCRIPT_PS\
"varying mediump vec2 texCoord;\n"\
"varying mediump vec2 logoTexCoord;\n"\
"varying mediump vec2 subtitleTexCoord;\n"\
"uniform mediump vec2 yCoord;\n"\
"uniform mediump vec2 uCoord;\n"\
"uniform mediump vec2 vCoord;\n"\
"uniform mediump mat4 blendtest;\n"\
" mediump mat4 blendfactor_c;\n"\
"\n"\
"uniform sampler2D txture;\n"\
"uniform sampler2D logoTexture;\n"\
"uniform sampler2D subtitleTexture;\n"\
"uniform sampler2D txture_y;\n"\
"uniform sampler2D txture_u;\n"\
"uniform sampler2D txture_v;\n"\
"uniform int use_yuv;\n"\
"lowp float y,u,v;\n"\
"lowp vec2 sub_coord;\n"\
"lowp vec4 tmp_yuv;\n"\
"const lowp mat3 cvrt_mtx = mat3(vec3(1.164,1.164,1.164),vec3(0.0,-0.391,2.018),vec3(1.596,-0.813,0.0));\n"\
"const lowp vec3 diff = vec3(0.0625,0.5,0.5);\n"\
"varying highp float idx;\n"\
"int cnt;\n"\
"lowp float tmp_y[4];\n"\
"lowp vec4 logo_color;\n"\
"lowp vec4 subtitle_color;\n"\
"lowp float correct;\n"\
"\n"\
"void main()\n"\
"{\n"\
"   blendfactor_c =blendtest;\n"\
"   //blendfactor_c=mat4(2.2875,-0.7154,-0.0721,0.5,-0.2125,2.2846,-0.0721,0.5,-0.2125,-0.7154,2.9279,0.5,0.0,0.0,0.0,1.0);\n"\
"	//subtitle_color = texture2D(subtitleTexture,subtitleTexCoord);\n"\
"	tmp_yuv.x = texture2D(txture_y,texCoord).a;\n"\
"	tmp_yuv.y = texture2D(txture_u,texCoord).a;\n"\
"	tmp_yuv.z = texture2D(txture_v,texCoord).a;\n"\
"   correct = min(tmp_yuv.x*30.0,1.0);\n"\
"   //tmp_yuv.xyz = max(tmp_yuv.xyz,diff);\n"\
"   tmp_yuv.w=1.0;\n"\
"	//gl_FragColor.a = 1.0;\n"\
"   gl_FragColor =tmp_yuv*blendfactor_c;\n"\
"   gl_FragColor.a = correct;\n"\
"	//gl_FragColor.rgb = vec3(1.0,0.0,0.0);\n"\
"}\n"\
""
#define YUV_TEXTURE_SCRIPT_SUBTITLE_PS\
"varying mediump vec2 texCoord;\n"\
"varying mediump vec2 logoTexCoord;\n"\
"varying mediump vec2 subtitleTexCoord;\n"\
"uniform mediump vec2 yCoord;\n"\
"uniform mediump vec2 uCoord;\n"\
"uniform mediump vec2 vCoord;\n"\
"uniform mediump mat4 blendtest;\n"\
" mediump mat4 blendfactor_c;\n"\
"\n"\
"uniform sampler2D txture;\n"\
"uniform sampler2D logoTexture;\n"\
"uniform sampler2D subtitleTexture;\n"\
"uniform sampler2D txture_y;\n"\
"uniform sampler2D txture_u;\n"\
"uniform sampler2D txture_v;\n"\
"uniform int use_yuv;\n"\
"lowp float y,u,v;\n"\
"lowp vec2 sub_coord;\n"\
"lowp vec4 tmp_yuv;\n"\
"const lowp mat3 cvrt_mtx = mat3(vec3(1.164,1.164,1.164),vec3(0.0,-0.391,2.018),vec3(1.596,-0.813,0.0));\n"\
"const lowp vec3 diff = vec3(0.0625,0.5,0.5);\n"\
"varying highp float idx;\n"\
"int cnt;\n"\
"lowp float tmp_y[4];\n"\
"lowp vec4 logo_color;\n"\
"lowp vec4 subtitle_color;\n"\
"lowp float correct;\n"\
"\n"\
"void main()\n"\
"{\n"\
"   blendfactor_c =blendtest;\n"\
"   //blendfactor_c=mat4(2.2875,-0.7154,-0.0721,0.5,-0.2125,2.2846,-0.0721,0.5,-0.2125,-0.7154,2.9279,0.5,0.0,0.0,0.0,1.0);\n"\
"	subtitle_color = texture2D(subtitleTexture,subtitleTexCoord);\n"\
"	tmp_yuv.x = texture2D(txture_y,texCoord).a;\n"\
"	tmp_yuv.y = texture2D(txture_u,texCoord).a;\n"\
"	tmp_yuv.z = texture2D(txture_v,texCoord).a;\n"\
"   tmp_yuv.xyz = max(tmp_yuv.xyz,diff);\n"\
"   tmp_yuv.w=1.0;\n"\
"   gl_FragColor =tmp_yuv*blendfactor_c;\n"\
"	gl_FragColor.rgb = mix(gl_FragColor.rgb, subtitle_color.rgb, subtitle_color.a);\n"\
"	//gl_FragColor.rgb = vec3(1.0,0.0,0.0);\n"\
"}\n"\
""


#define RGBA_TEXTURE_SCRIPT\
	"varying highp vec2 texCoord;\n"\
	"varying highp vec2 logoTexCoord;\n"\
    "varying highp vec2 subtitleTexCoord;\n"\
	"uniform highp vec2 yCoord;\n"\
	"uniform highp vec2 uCoord;\n"\
	"uniform highp vec2 vCoord;\n"\
	"\n"\
	"uniform sampler2D txture;\n"\
	"uniform sampler2D logoTexture;\n"\
	"uniform sampler2D subtitleTexture;\n"\
	"uniform sampler2D txture_y;\n"\
	"uniform sampler2D txture_u;\n"\
	"uniform sampler2D txture_v;\n"\
	"uniform int use_yuv;\n"\
	"lowp float y,u,v;\n"\
	"lowp vec2 sub_coord;\n"\
	"lowp vec3 tmp_yuv;\n"\
	"const lowp mat3 cvrt_mtx = mat3(vec3(1.164,1.164,1.164),vec3(0.0,-0.391,2.018),vec3(1.596,-0.813,0.0));\n"\
	"const lowp vec3 diff = vec3(0.0625,0.5,0.5);\n"\
	"varying highp float idx;\n"\
	"int cnt;\n"\
	"lowp float tmp_y[4];\n"\
	"lowp vec4 logo_color;\n"\
	"lowp vec4 subtitle_color;\n"\
	"\n"\
	"void main()\n"\
	"{\n"\
	"	logo_color = texture2D(logoTexture,logoTexCoord);\n"\
	"	subtitle_color = texture2D(subtitleTexture,subtitleTexCoord);\n"\
	"	gl_FragColor.rgb = texture2D(txture_y,texCoord).rgb;\n"\
	"	gl_FragColor.rgb = mix(gl_FragColor.rgb, logo_color.rgb, logo_color.a);\n"\
	"	gl_FragColor.rgb = mix(gl_FragColor.rgb, subtitle_color.rgb, subtitle_color.a);\n"\
	"	gl_FragColor.a = 1.0;\n"\
    "	//gl_FragColor.rgb = vec3(1.0,0.0,0.0);\n"\
	"}\n"\
	""
#else

#define YUV_VERTEX_SCRIPT\
"attribute vec4 vPosition;\n"\
"attribute vec2 vTexCoord;\n"\
"attribute vec2 vLogoTexCoord;\n"\
"varying highp vec2 texCoord;\n"\
"varying highp vec2 logoTexCoord;\n"\
"varying highp vec2 last_Coord;\n"\
"void main()\n"\
"{\n"\
"	last_Coord = texCoord;\n"\
"	texCoord = vTexCoord;\n"\
"	logoTexCoord = vLogoTexCoord;\n"\
"	gl_Position = vPosition;\n"\
"}\n"\

#define YUV_TEXTURE_SCRIPT\
"varying highp vec2 texCoord;\n"\
"varying highp vec2 logoTexCoord;\n"\
"uniform highp vec2 yCoord;\n"\
"uniform highp vec2 uCoord;\n"\
"uniform highp vec2 vCoord;\n"\
"\n"\
"uniform sampler2D txture;\n"\
"uniform sampler2D logoTexture;\n"\
"uniform sampler2D txture_y;\n"\
"uniform sampler2D txture_u;\n"\
"uniform sampler2D txture_v;\n"\
"varying highp vec2 last_Coord;\n"\
"uniform int use_yuv;\n"\
"lowp float y,u,v;\n"\
"lowp vec2 sub_coord;\n"\
"lowp vec3 tmp_yuv;\n"\
"const lowp mat3 cvrt_mtx = mat3(vec3(1.164,1.164,1.164),vec3(0.0,-0.391,2.018),vec3(1.596,-0.813,0.0));\n"\
"const lowp vec3 diff = vec3(0.0625,0.5,0.5);\n"\
"varying highp float idx;\n"\
"int cnt;\n"\
"lowp float tmp_y[4];\n"\
"lowp vec4 logo_color;\n"\
"\n"\
"void main()\n"\
"{\n"\
"	logo_color = texture2D(logoTexture,logoTexCoord);\n"\
"	if(texCoord.x > last_Coord.x)\n"\
"		gl_FragColor.rgb = texture2D(txture_y,texCoord).aaa;\n"\
"	else\n"\
"		gl_FragColor.rgb = vec3(1.0,0.0,0.0);\n"\
"	//tmp_yuv.x = texture2D(txture_y,texCoord).a;\n"\
"	//tmp_yuv.y = texture2D(txture_u,texCoord).a;\n"\
"	//tmp_yuv.z = texture2D(txture_v,texCoord).a;\n"\
"	//gl_FragColor.rgb = mix(cvrt_mtx * (tmp_yuv - diff), logo_color.rgb, logo_color.a);\n"\
"	gl_FragColor.a = 1.0;\n"\
"}\n"\
""

#define RGBA_TEXTURE_SCRIPT\
"varying highp vec2 texCoord;\n"\
"varying highp vec2 logoTexCoord;\n"\
"uniform highp vec2 yCoord;\n"\
"uniform highp vec2 uCoord;\n"\
"uniform highp vec2 vCoord;\n"\
"\n"\
"uniform sampler2D txture;\n"\
"uniform sampler2D logoTexture;\n"\
"uniform sampler2D txture_y;\n"\
"uniform sampler2D txture_u;\n"\
"uniform sampler2D txture_v;\n"\
"uniform int use_yuv;\n"\
"lowp float y,u,v;\n"\
"lowp vec2 sub_coord;\n"\
"lowp vec3 tmp_yuv;\n"\
"const lowp mat3 cvrt_mtx = mat3(vec3(1.164,1.164,1.164),vec3(0.0,-0.391,2.018),vec3(1.596,-0.813,0.0));\n"\
"const lowp vec3 diff = vec3(0.0625,0.5,0.5);\n"\
"varying highp float idx;\n"\
"int cnt;\n"\
"lowp float tmp_y[4];\n"\
"lowp vec4 logo_color;\n"\
"\n"\
"void main()\n"\
"{\n"\
"	//logo_color = texture2D(logoTexture,logoTexCoord);\n"\
"	gl_FragColor.rgb = texture2D(txture_y,texCoord).aaa;\n"\
"	gl_FragColor.a = 1.0;\n"\
"	//gl_FragColor.rgb = mix(gl_FragColor.rgb, logo_color.rgb, logo_color.a);\n"\
"}\n"\
""
#endif




static void end_yuv_texture(YUV_CTX* p_yuv_ctx)
{
	if (p_yuv_ctx->yuvTexture[0])
	{
		glDeleteTextures(NUM_YUV_TEXTURES, p_yuv_ctx->yuvTexture);
		ekk_free(p_yuv_ctx->yuvTextureBuf);
		p_yuv_ctx->yuvTextureBuf = 0;
	}
}

static void prepare_yuv_texture(YUV_CTX* p_yuv_ctx, GLuint program, int raw_width, int raw_height, GLenum begin_text,GLuint* program_array)
{
	end_yuv_texture(p_yuv_ctx);
	EKK_VIDEO_FRAME_TYPE vft = EKK_NONE;
	ekk_get_video_frame_type(&vft);
    int i;
    GLuint default_program;
	if(vft == EKK_Y4UV4)
	{
		p_yuv_ctx->uv_pix_type = GL_LUMINANCE_ALPHA;
	}
	else
	{
		p_yuv_ctx->uv_pix_type = GL_ALPHA;
	}
	
	glGenTextures(NUM_YUV_TEXTURES, p_yuv_ctx->yuvTexture);
	
	// Get uniform locations
    default_program=program;
    for(i=0;i<4;i++)
    {
        program=program_array[i];
        glUseProgram(program);
	p_yuv_ctx->uniform_yuv[TEXTURE_Y] = glGetUniformLocation(program, "txture_y");
	p_yuv_ctx->uniform_yuv[TEXTURE_U] = glGetUniformLocation(program, "txture_u");
	p_yuv_ctx->uniform_yuv[TEXTURE_V] = glGetUniformLocation(program, "txture_v");
	
	// Update uniform value
	glUniform1i(p_yuv_ctx->uniform_yuv[TEXTURE_Y], (begin_text - GL_TEXTURE0) + Y_LEVEL);
	glUniform1i(p_yuv_ctx->uniform_yuv[TEXTURE_U], (begin_text - GL_TEXTURE0) + U_LEVEL);
	glUniform1i(p_yuv_ctx->uniform_yuv[TEXTURE_V], (begin_text - GL_TEXTURE0) + V_LEVEL);
	
	p_yuv_ctx->yuvTextureBuf = ekk_malloc(raw_width * raw_height * 4);

	//set it to black
	ekk_memset(p_yuv_ctx->yuvTextureBuf, 16, raw_width * raw_height * 4);
	
	glActiveTexture(begin_text + Y_LEVEL);
	glBindTexture(GL_TEXTURE_2D, p_yuv_ctx->yuvTexture[Y_LEVEL]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexImage2D(GL_TEXTURE_2D, 0/*Y_LEVEL*/, GL_ALPHA, raw_width, raw_height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, p_yuv_ctx->yuvTextureBuf);
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	// Enable use of the texture
	glEnable(GL_TEXTURE_2D);

#if	1
	ekk_memset(p_yuv_ctx->yuvTextureBuf, 127, raw_width * raw_height * 4);
	
	glActiveTexture(begin_text + U_LEVEL);
	glBindTexture(GL_TEXTURE_2D, p_yuv_ctx->yuvTexture[U_LEVEL]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexImage2D(GL_TEXTURE_2D, 0/*Y_LEVEL*/, p_yuv_ctx->uv_pix_type, raw_width/2, raw_height/2, 0, p_yuv_ctx->uv_pix_type, GL_UNSIGNED_BYTE, p_yuv_ctx->yuvTextureBuf);
	// Enable use of the texture
	glEnable(GL_TEXTURE_2D);
	
	ekk_memset(p_yuv_ctx->yuvTextureBuf, 127, raw_width * raw_height * 4);
	
	glActiveTexture(begin_text + V_LEVEL);
	glBindTexture(GL_TEXTURE_2D, p_yuv_ctx->yuvTexture[V_LEVEL]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexImage2D(GL_TEXTURE_2D, 0/*Y_LEVEL*/, p_yuv_ctx->uv_pix_type, raw_width/2, raw_height/2, 0, p_yuv_ctx->uv_pix_type, GL_UNSIGNED_BYTE, p_yuv_ctx->yuvTextureBuf);
	// Enable use of the texture
	glEnable(GL_TEXTURE_2D);
#endif
		
	ekk_free(p_yuv_ctx->yuvTextureBuf);
	p_yuv_ctx->yuvTextureBuf = 0;
	
	p_yuv_ctx->raw_yuv_width = raw_width;
	p_yuv_ctx->raw_yuv_height = raw_height;
    }
    program=default_program;
    glUseProgram(program);
}

static void inline do_yuv_texture(YUV_CTX* p_yuv_ctx, GLuint program, int sub_x, int sub_y, int sub_line_size_y, int sub_line_size_u, int sub_line_size_v, int sub_height, GLenum begin_text, unsigned char* sub_img_y, unsigned char* sub_img_u, unsigned char* sub_img_v)
{
	int uv_x, uv_y, uv_h;
	GLfloat start_coord[2], end_coord[2];
	start_coord[0] = ((GLfloat)sub_x) / (GLfloat)p_yuv_ctx->raw_yuv_width;
	start_coord[1] = ((GLfloat)sub_y) / (GLfloat)p_yuv_ctx->raw_yuv_height;
	end_coord[0] = ((GLfloat)(sub_x + sub_line_size_y)) / (GLfloat)p_yuv_ctx->raw_yuv_width;
	end_coord[1] = ((GLfloat)(sub_y + sub_height)) / (GLfloat)p_yuv_ctx->raw_yuv_height;
	
	// Get uniform locations
	p_yuv_ctx->uniform_yuv[TEXTURE_Y] = glGetUniformLocation(program, "txture_y");
	p_yuv_ctx->uniform_yuv[TEXTURE_U] = glGetUniformLocation(program, "txture_u");
	p_yuv_ctx->uniform_yuv[TEXTURE_V] = glGetUniformLocation(program, "txture_v");
	
	// Update uniform value
	glUniform1i(p_yuv_ctx->uniform_yuv[TEXTURE_Y], (begin_text - GL_TEXTURE0) + Y_LEVEL);
	glUniform1i(p_yuv_ctx->uniform_yuv[TEXTURE_U], (begin_text - GL_TEXTURE0) + U_LEVEL);
	glUniform1i(p_yuv_ctx->uniform_yuv[TEXTURE_V], (begin_text - GL_TEXTURE0) + V_LEVEL);

	glActiveTexture(begin_text + Y_LEVEL);
	glTexSubImage2D(GL_TEXTURE_2D, 0, (sub_x >= 2 ? sub_x : 2), (sub_y >= 2 ? sub_y : 2), sub_line_size_y , sub_height, GL_ALPHA, GL_UNSIGNED_BYTE, sub_img_y);
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glEnable(GL_TEXTURE_2D);
	//glEnable(GL_BLEND);
#if 1
	uv_x = sub_x>>1;
	uv_y = sub_y>>1;
	uv_h = sub_height>>1;
	
	glActiveTexture(begin_text + U_LEVEL);
	glTexSubImage2D(GL_TEXTURE_2D, 0, (uv_x >= 1? uv_x : 1) , (uv_y >= 1 ? uv_y : 1), sub_line_size_u, uv_h, p_yuv_ctx->uv_pix_type, GL_UNSIGNED_BYTE, sub_img_u);
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glEnable(GL_TEXTURE_2D);
	//glEnable(GL_BLEND);
	
	
	glActiveTexture(begin_text + V_LEVEL);
	//glBindTexture(GL_TEXTURE_2D, spriteTexture[V_LEVEL]);
	glTexSubImage2D(GL_TEXTURE_2D, 0, (uv_x >= 1? uv_x : 1) , (uv_y >= 1 ? uv_y : 1), sub_line_size_v, uv_h, p_yuv_ctx->uv_pix_type, GL_UNSIGNED_BYTE, sub_img_v);
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glEnable(GL_TEXTURE_2D);
	//glEnable(GL_BLEND);
#endif
	
#if TEST_SAVE_FILE
	char test_path[1024] = {0};
	ekk_sprintf(test_path, "%s%s", ekk_getcwd(), "/Documents/tttt.aaa.yuv");
	FILE* hf = fopen(test_path, "ab+");
	fwrite(sub_img_y, 1, sub_width * sub_height, hf);
	fwrite(sub_img_u, 1, uv_w * uv_h, hf);
	fwrite(sub_img_v, 1, uv_w * uv_h, hf);
	fclose(hf);
#endif
	
}

static void fill_test_color(char* buffer, int sw, int sh, int x, int y, int w, int h)
{
	int pos = 0;
	char clr[4] = {0xff, 0x00, 0x55, 0xff};
	for (int i = 0; i < h; ++i)
	{
		pos = (sw * y + x) * 4;
		for (int j = 0; j < w; ++j)
		{
			buffer[pos++] = clr[0];
			buffer[pos++] = clr[1];
			buffer[pos++] = clr[2];
			buffer[pos++] = clr[3];
		}
	}
	
}

#if TEST_MULTI_THREAD
void es2_do_multi_thread_test()
{
}
#endif

/////////////////////////////////////////////////

//static unsigned char sub_rgb[1024*1024*4] = {0}; 


typedef struct tagRGBA {
	char R;
	char G;
	char B;
	char A;
} RGBA,*LPRGBA;

// attribute index
enum {
    ATTRIB_VERTEX,
    ATTRIB_COORD,
    ATTRIB_LOGOCOORD,
	ATTRIB_SUBTITLECOORD,
    ATTRIB_TEXVEC,
    NUM_ATTRIBUTES
};

@interface ES2Renderer (PrivateMethods)
- (BOOL)loadShaders;
- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type ;
- (BOOL)linkProgram:(GLuint)prog;
- (BOOL)validateProgram:(GLuint)prog;
@end

@implementation ES2Renderer

/*@synthesize picture_linesize_y;
@synthesize picture_linesize_u;
@synthesize picture_linesize_v;*/

// Create an OpenGL ES 2.0 context


GLuint LoadShader(GLenum type, const char *shaderSrc)
{
	GLuint shader;
	GLint compiled;
	// Create the shader object
    shader = glCreateShader(type);
	if(shader == 0)
		return 0;
	// Load the shader source
	glShaderSource(shader, 1, &shaderSrc, NULL);
    // Compile the shader
	glCompileShader(shader);
    // Check the compile status
	glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
	if(!compiled)
	{
		GLint infoLen = 0;
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
		if(infoLen > 1)
		{
			char* infoLog = ekk_malloc(sizeof(char) * infoLen);
			glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
			printf("Error compiling shader:\n%s\n", infoLog);
//			esLogMessage("Error compiling shader:\n%s\n", infoLog);
			ekk_free(infoLog);
		}
		glDeleteShader(shader);
		return 0;
	}
	return shader;
}

- (void)init_variables
{
#define ZERO(x) ekk_memset(x, 0, sizeof(x));
	yuv_ctx.use_yuv = true;
	
	ZERO(yuv_ctx.uniform_yuv);
	ZERO(yuv_ctx.yuvTexture);
	yuv_ctx.yuvTextureBuf = 0;
	
	yuv_ctx.raw_yuv_width = 0;
	yuv_ctx.raw_yuv_height = 0;
	yuv_ctx.sub_yuv_x = 0;
	yuv_ctx.sub_yuv_y = 0;
	yuv_ctx.sub_yuv_w = 0;
	yuv_ctx.sub_yuv_h = 0;
	yuv_ctx.uv_pix_type = GL_BYTE;
	
	sub_rgb = NULL;
	texture_size_x = 0;
	texture_size_y = 0;
	sub_img_width = 0;
	sub_img_height = 0;
	
	spriteVertices[0] = -1.f;
	spriteVertices[1] = 1.f;
	spriteVertices[2] = 1.f;
	spriteVertices[3] = 1.f;
	spriteVertices[4] = -1.f;
	spriteVertices[5] = -1.f;
	spriteVertices[6] = 1.f;
	spriteVertices[7] = -1.f;
	
	g_magnify_ratio = 0.f;
	
	// Sets up an array of values for the texture coordinates.
	spriteTexcoords[0] = 0.0;
	spriteTexcoords[1] = 0.0;
	spriteTexcoords[2] = 1.0;
	spriteTexcoords[3] = 0.0;
	spriteTexcoords[4] = 0.0;
	spriteTexcoords[5] = 1.0;
	spriteTexcoords[6] = 1.0;
	spriteTexcoords[7] = 1.0;
	
	logoTexcoords[0] = 0.0;
	logoTexcoords[1] = 0.0;
	logoTexcoords[2] = 1.0;
	logoTexcoords[3] = 0.0;
	logoTexcoords[4] = 0.0;
	logoTexcoords[5] = 1.0;
	logoTexcoords[6] = 1.0;
	logoTexcoords[7] = 1.0;
	
	subtitleTexcoords[0] = 0.0;
	subtitleTexcoords[1] = 0.0;
	subtitleTexcoords[2] = 1.0;
	subtitleTexcoords[3] = 0.0;
	subtitleTexcoords[4] = 0.0;
	subtitleTexcoords[5] = 1.0;
	subtitleTexcoords[6] = 1.0;
	subtitleTexcoords[7] = 1.0;
	
	texture_size_x_real = 0;
	picture_linesize_y = 0;
	picture_linesize_u = 0;
	picture_linesize_v = 0;
	
	logo_texture = 0;
	subtitle_texture = 0;
	
	need_update_rect = 0;
}

// Create an OpenGL ES 2.0 context
- (id)init
{
	LOG_FUNC_CALL();
	
	[self init_variables];
    if ((self = [super init]))
    {
        context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];

        if (!context || ![EAGLContext setCurrentContext:context] || ![self loadShaders])
        {
            [self release];
            return nil;
        }
        
        video_logo_buffer = NULL;
		size_mode = 0;
		vec_ratio = 0;

        // Create default framebuffer object. The backing will be allocated for the current layer in -resizeFromLayer
		glGenFramebuffers(1, &defaultFramebuffer);
        glGenRenderbuffers(1, &colorRenderbuffer);
        glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
        glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);
        
        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        {
            NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
        }

//	glGenTextures(1, &spriteTexture);
//	// Bind the texture name. 
//	glBindTexture(GL_TEXTURE_2D, spriteTexture);
//		
//	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
//		
#if 1
        for(int i=0;i<4;i++)
        {
            program=program_array[i];
            uniforms=uniforms_array[i];
        glUseProgram(program);

		glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, spriteVertices);
		glEnableVertexAttribArray(ATTRIB_VERTEX);
		glVertexAttribPointer(ATTRIB_COORD, 2, GL_FLOAT, 0, 0, spriteTexcoords);
		glEnableVertexAttribArray(ATTRIB_COORD);
		glVertexAttribPointer(ATTRIB_LOGOCOORD, 2, GL_FLOAT, 0, 0, logoTexcoords);
		glEnableVertexAttribArray(ATTRIB_LOGOCOORD);
		glVertexAttribPointer(ATTRIB_SUBTITLECOORD, 2, GL_FLOAT, 0, 0, subtitleTexcoords);
		glEnableVertexAttribArray(ATTRIB_SUBTITLECOORD);
//	glUniform1i(uniforms[UNIFORM_TEXTURE], spriteTexture);	
		glUniform1i(uniforms[UNIFORM_TEXTURE], 0);	
		glUniform1i(uniforms[UNIFORM_LOGO_TEXTURE], 1);	
		glUniform1i(uniforms[UNIFORM_SUBTITLE_TEXTURE], 2);	
		glUniform1i(uniforms[UNIFORM_USELOGO], 0);

		glEnableVertexAttribArray(0);
        }
        program=program_array[0];
        uniforms=uniforms_array[0];
        glUseProgram(program);
#endif
		
//	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	
		
	// Enable use of the texture
//	glEnable(GL_TEXTURE_2D);
        lock = (void*)ekk_mutex_create();
    }
    play_now =1;
    default_contrast=1.0;
    default_brightness=1.0;
    default_saturation=1.0;
    return self;
}

- (long)render_subtitle_pic:(unsigned char *)subtitle_pic_buffer
{
	// This application only creates a single context which is already set current at this point.
    // This call is redundant, but needed if dealing with multiple contexts.
    [EAGLContext setCurrentContext:context];
	
    // This application only creates a single default framebuffer which is already bound at this point.
    // This call is redundant, but needed if dealing with multiple framebuffers.
    glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
    glViewport(0, 0, backingWidth, backingHeight);
	
	//glClear(GL_COLOR_BUFFER_BIT);
	glUniform1i(uniforms[UNIFORM_SUBTITLE_TEXTURE], 2);
		
	//fill_test_color(video_buffer , src_width, src_height, 30, 60, 100, 100);
		
	glActiveTexture(GL_TEXTURE2);
	glTexSubImage2D(GL_TEXTURE_2D, 0, 1, 1, subtitle_width, subtitle_height,GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char*)subtitle_pic_buffer/*packed_rgb_test*/);
	glEnable(GL_TEXTURE_2D);
	
    // Use shader program
	glUseProgram(program);
	
	//    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indici);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
	
    // This application only creates a single color renderbuffer which is already bound at this point.
    // This call is redundant, but needed if dealing with multiple renderbuffers.
    glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER];
	
	return EKK_ERROR_NONE;
}

- (long)render_buffer:(unsigned char *)video_buffer y:(int)linesize_y u:(int)linesize_u v:(int)linesize_v;
{
    // Replace the implementation of this method to do your own custom drawing

   /*
   static const GLfloat squareVertices[] = {
        -1.0f, -1.0f,
         1.0f, -1.0f,
        -1.0f,  1.0f,
         1.0f,  1.0f,
    };

    static const GLubyte squareColors[] = {
        255, 255,   0, 255,
        0,   255, 255, 255,
        0,     0,   0,   0,
        255,   0, 255, 255,
    };
	
	static const GLfloat squareCoord[] = {
		0.0, 1.0,
		1.0, 1.0,
		0.0, 0.0,
		1.0, 0.0
	};

	static const GLushort indici[] = {0, 1, 2, 1, 2, 3};
*/
	int src_width, src_height, width, height;	
	int des_x, des_y;
	unsigned char *des_img_y, *des_img_u, *des_img_v;
    
    ekk_mutex_lock((EKK_MUTEX_HANDLE)lock);
    if(ps_changed == 100 && play_now==100 )
    {
        GLint pathindex;
        if((program == program_array[0] )||(program == program_array[2]))
        {
            program = program_array[2];
            uniforms = uniforms_array[2];
        }else
        {
            program = program_array[3];
            uniforms = uniforms_array[3];
        }
        glUseProgram(program);
        
        pathindex=glGetUniformLocation(program,"blendtest");
        glUniformMatrix4fv(pathindex, 1,GL_FALSE, mask);
        ps_changed =0;
        play_now=1;
    }
    play_now=100;
	if(need_update_rect)
	{
		need_update_rect = 0;
		[self set_video_rect:&new_video_rect src_rect_name:&new_video_src_rect];
		if(video_logo_buffer)
		[self set_video_logo:&video_logo_rect logo_buffer_name:video_logo_buffer logo_size_name:&video_logo_size];	
	}
   
	src_width = video_src_rect.size.width;
	src_height = video_src_rect.size.height;
	width = video_rect.size.width;
	height = video_rect.size.height;

    // This application only creates a single context which is already set current at this point.
    // This call is redundant, but needed if dealing with multiple contexts.
    [EAGLContext setCurrentContext:context];
	
	EKK_VIDEO_FRAME_TYPE vft = EKK_NONE;
	ekk_get_video_frame_type(&vft);
	if(vft == EKK_Y4UV4)
	{
		linesize_u >>= 1;
		linesize_v = linesize_u;
	}

    // This application only creates a single default framebuffer which is already bound at this point.
    // This call is redundant, but needed if dealing with multiple framebuffers.
    glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
    glViewport(0, 0, backingWidth, backingHeight);

    /*static GLfloat ttttt;
    ttttt += 0.1;
    ttttt = ttttt - ((GLfloat)((GLint)ttttt));
    glClearColor(ttttt, 0.4f, 0.5f, 1.0f);*/
    
    static BOOL test = NO;
    //test = !test;
    
    if (test)
    {
        glClearColor(1.0f, 0.2f, 0.5f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
    }
    else
    {
        
    
	glClear(GL_COLOR_BUFFER_BIT);

	
	des_x = (sub_img_width - src_width) >> 1;
	des_y = (sub_img_height - src_height) >> 1;
	
	// Prevent deviation of float calculation
	des_x = des_x >= 0 ? des_x : 0;
	des_y = des_y >= 0 ? des_y : 0;
	
	picture_linesize_y = linesize_y;
	picture_linesize_u = linesize_u;
	picture_linesize_v = linesize_v;
	
	if(yuv_ctx.use_yuv)
	{
		if(yuv_ctx.uv_pix_type == GL_LUMINANCE_ALPHA)
		{
			des_img_y = video_buffer;
			des_img_u = video_buffer + picture_linesize_y * src_height - 1;
			des_img_v = des_img_u + 1;//des_img_v = des_img_u + (src_width * src_height / 4);//
		}
		else
		{
			des_img_y = video_buffer;
			des_img_u = video_buffer + picture_linesize_y * src_height;
			des_img_v = des_img_u + (picture_linesize_u * src_height / 2);
		}

		do_yuv_texture(&yuv_ctx, program, des_x, des_y, picture_linesize_y, picture_linesize_u, picture_linesize_v, src_height, GL_TEXTURE3, des_img_y, des_img_u, des_img_v);
	}
	else
	{
		glUniform1i(yuv_ctx.uniform_yuv[TEXTURE_Y], 0);
		glUniform1i(uniforms[UNIFORM_LOGO_TEXTURE], 1);
		
		//fill_test_color(video_buffer , src_width, src_height, 30, 60, 100, 100);
		
		glActiveTexture(GL_TEXTURE0);
		glTexSubImage2D(GL_TEXTURE_2D, 0, (des_x >= 1 ? des_x : 1), (des_y >= 1 ? des_y : 1 ), src_width, src_height,GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char*)video_buffer/*packed_rgb_test*/);
		glEnable(GL_TEXTURE_2D);
	}
        
    // Use shader program
	glUseProgram(program);
        
        
        
        
    }/////////

    
    
    
    // DEBUG macro must be defined in your debug configurations if that's not already the case.
#if defined(DEBUG)
//    if (![self validateProgram:program])
//    {
//        NSLog(@"Failed to validate program: %d", program);	
//        return;
//    }
#endif

//    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indici);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    // This application only creates a single color renderbuffer which is already bound at this point.
    // This call is redundant, but needed if dealing with multiple renderbuffers.
    glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
    
    [context presentRenderbuffer:GL_RENDERBUFFER];
    ekk_mutex_unlock((EKK_MUTEX_HANDLE)lock);
	
	return EKK_ERROR_NONE;
}

- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file
{
    GLint status;
    const GLchar *source;

    source = (GLchar *)[[NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil] UTF8String];
    if (!source)
    {
//        NSLog(@"Failed to load vertex shader");
        return FALSE;
    }

    *shader = glCreateShader(type);
    glShaderSource(*shader, 1, &source, NULL);
    glCompileShader(*shader);

#if defined(DEBUG)
    GLint logLength;
    glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0)
    {
        GLchar *log = (GLchar *)ekk_malloc(logLength);
        glGetShaderInfoLog(*shader, logLength, &logLength, log);
//        NSLog(@"Shader compile log:\n%s", log);
        ekk_free(log);
    }
#endif

    glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
    if (status == 0)
    {
        glDeleteShader(*shader);
        return FALSE;
    }

    return TRUE;
}

- (BOOL)linkProgram:(GLuint)prog
{
    GLint status;

    glLinkProgram(prog);

#if 1
    //defined(DEBUG)
    GLint logLength;
    glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0)
    {
        GLchar *log = (GLchar *)ekk_malloc(logLength);
        glGetProgramInfoLog(prog, logLength, &logLength, log);
        NSLog(@"Program link log:\n%s", log);
        ekk_free(log);
    }
#endif

    glGetProgramiv(prog, GL_LINK_STATUS, &status);
    if (status == 0)
        return FALSE;

    return TRUE;
}

- (BOOL)validateProgram:(GLuint)prog
{
    GLint logLength, status;

    glValidateProgram(prog);
    glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0)
    {
        GLchar *log = (GLchar *)ekk_malloc(logLength);
        glGetProgramInfoLog(prog, logLength, &logLength, log);
//        NSLog(@"Program validate log:\n%s", log);
        ekk_free(log);
    }

    glGetProgramiv(prog, GL_VALIDATE_STATUS, &status);
    if (status == 0)
        return FALSE;

    return TRUE;
}

- (BOOL)loadShaders
{
    GLuint vertShader, fragShader;
    int i;
    NSString *vertShaderPathname, *fragShaderPathname;
	
	GLbyte vSharderStr[] = 
		YUV_VERTEX_SCRIPT;
	
	GLbyte* fShaderStr = 0;
	int fs_len = 0;
	
	if (yuv_ctx.use_yuv)
	{
        for(i=0;i<4;i++)
            program_array[i]=glCreateProgram();
        
        vertShader = LoadShader(GL_VERTEX_SHADER, vSharderStr);
        // script
		fs_len = ekk_strlen(YUV_TEXTURE_SCRIPT) + 1;
		fShaderStr = ekk_malloc(fs_len);
		ekk_strncpy(fShaderStr, YUV_TEXTURE_SCRIPT, fs_len);
        fragShader = LoadShader(GL_FRAGMENT_SHADER, fShaderStr);
        
        glAttachShader(program_array[0], vertShader);
        glAttachShader(program_array[0], fragShader);
        //subtitle
        fs_len = ekk_strlen(YUV_TEXTURE_SCRIPT_SUBTITLE) + 1;
		fShaderStr = ekk_malloc(fs_len);
		ekk_strncpy(fShaderStr, YUV_TEXTURE_SCRIPT_SUBTITLE       , fs_len);
        fragShader = LoadShader(GL_FRAGMENT_SHADER, fShaderStr);
        
        glAttachShader(program_array[1], vertShader);
        glAttachShader(program_array[1], fragShader);
        //ps
        fs_len = ekk_strlen(YUV_TEXTURE_SCRIPT_PS) + 1;
		fShaderStr = ekk_malloc(fs_len);
		ekk_strncpy(fShaderStr, YUV_TEXTURE_SCRIPT_PS, fs_len);
        fragShader = LoadShader(GL_FRAGMENT_SHADER, fShaderStr);
        
        glAttachShader(program_array[2], vertShader);
        glAttachShader(program_array[2], fragShader);
        //subtitle +ps
        fs_len = ekk_strlen(YUV_TEXTURE_SCRIPT_SUBTITLE_PS) + 1;
		fShaderStr = ekk_malloc(fs_len);
		ekk_strncpy(fShaderStr, YUV_TEXTURE_SCRIPT_SUBTITLE_PS, fs_len);
        fragShader = LoadShader(GL_FRAGMENT_SHADER, fShaderStr);
        
        glAttachShader(program_array[3], vertShader);
        glAttachShader(program_array[3], fragShader);
        for(i=0;i<4;i++)
        {
            glBindAttribLocation(program_array[i], ATTRIB_VERTEX, "vPosition");
            glBindAttribLocation(program_array[i], ATTRIB_COORD, "vTexCoord");
            glBindAttribLocation(program_array[i], ATTRIB_LOGOCOORD, "vLogoTexCoord");
            glBindAttribLocation(program_array[i], ATTRIB_SUBTITLECOORD, "vSubtitleTexCoord");
            if (![self linkProgram:program_array[i]])
            {
                //        NSLog(@"Failed to link program: %d", program);
                
                if (vertShader)
                {
                    glDeleteShader(vertShader);
                    vertShader = 0;
                }
                if (fragShader)
                {
                    glDeleteShader(fragShader);
                    fragShader = 0;
                }
                if (program_array[i])
                {
                    glDeleteProgram(program_array[i]);
                    program_array[i] = 0;
                }
                
                ekk_free(fShaderStr);
                return FALSE;
            }
            
            // Get uniform locations
            uniforms_array[i][UNIFORM_TEXTURE] = glGetUniformLocation(program_array[i], "texture");	
            uniforms_array[i][UNIFORM_LOGO_TEXTURE] = glGetUniformLocation(program_array[i], "logoTexture");
            uniforms_array[i][UNIFORM_SUBTITLE_TEXTURE] = glGetUniformLocation(program_array[i], "subtitleTexture");
            uniforms_array[i][UNIFORM_USELOGO] = glGetUniformLocation(program_array[i], "nUseLogo");
        }
        program =program_array[0];
        uniforms = uniforms_array[0];
	}
	else 
	{
		fs_len = ekk_strlen(RGBA_TEXTURE_SCRIPT) + 1;
		fShaderStr = ekk_malloc(fs_len);
		ekk_strncpy(fShaderStr, RGBA_TEXTURE_SCRIPT, fs_len);
        vertShader = LoadShader(GL_VERTEX_SHADER, vSharderStr);
        fragShader = LoadShader(GL_FRAGMENT_SHADER, fShaderStr);
        
        // Create shader program
        program_array[0] = glCreateProgram();
        
        // Attach vertex shader to program
        glAttachShader(program_array[0], vertShader);
        
        // Attach fragment shader to program
        glAttachShader(program_array[0], fragShader);
        
        // Bind attribute locations
        // this needs to be done prior to linking
        glBindAttribLocation(program_array[0], ATTRIB_VERTEX, "vPosition");
        glBindAttribLocation(program_array[0], ATTRIB_COORD, "vTexCoord");
        glBindAttribLocation(program_array[0], ATTRIB_LOGOCOORD, "vLogoTexCoord");
        glBindAttribLocation(program_array[0], ATTRIB_SUBTITLECOORD, "vSubtitleTexCoord");
        
        // Link program
        if (![self linkProgram:program_array[0]])
        {
            //        NSLog(@"Failed to link program: %d", program);
            
            if (vertShader)
            {
                glDeleteShader(vertShader);
                vertShader = 0;
            }
            if (fragShader)
            {
                glDeleteShader(fragShader);
                fragShader = 0;
            }
            if (program_array[0])
            {
                glDeleteProgram(program_array[0]);
                program_array[0] = 0;
            }
            
            ekk_free(fShaderStr);
            return FALSE;
        }
        
        // Get uniform locations
        uniforms_array[0][UNIFORM_TEXTURE] = glGetUniformLocation(program_array[0], "texture");	
        uniforms_array[0][UNIFORM_LOGO_TEXTURE] = glGetUniformLocation(program_array[0], "logoTexture");
        uniforms_array[0][UNIFORM_SUBTITLE_TEXTURE] = glGetUniformLocation(program_array[0], "subtitleTexture");
        uniforms_array[0][UNIFORM_USELOGO] = glGetUniformLocation(program_array[0], "nUseLogo");
        
        program=program_array[0];
        uniforms =uniforms_array[0];
	}
	
	

    // Release vertex and fragment shaders
    if (vertShader)
        glDeleteShader(vertShader);
    if (fragShader)
        glDeleteShader(fragShader);

	ekk_free(fShaderStr);
    return TRUE;
}

- (BOOL)resizeFromLayer:(CAEAGLLayer *)layer
{
    BOOL ret_val = YES;
    
    ekk_mutex_lock((EKK_MUTEX_HANDLE)lock);
    // Allocate color buffer backing based on the current layer size
    glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
    [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer];
    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);

    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
        ret_val = NO;
    }
    ekk_mutex_unlock((EKK_MUTEX_HANDLE)lock);

    return ret_val;
}

- (long)update_video_rect:(CGRect*)rect src_rect_name:(CGRect*)src_rect
{
	LOG_FUNC_CALL();
	
	if(rect && src_rect)
	{
		LOGDB(@"[TVOUT render]call update_video_rect rect(%d,%d,%d,%d),src_rect(%d,%d,%d,%d) is_first=%d",
			rect->left,rect->top,rect->right,rect->bottom,
			src_rect->left,src_rect->top,src_rect->right,src_rect->bottom);
	
		need_update_rect = 0;
		new_video_rect = *rect;
		new_video_src_rect = *src_rect;
		need_update_rect = 1;
	}
	return 0;
}
- (long)set_video_rect:(CGRect*)rect src_rect_name:(CGRect*)src_rect
{
	LOG_FUNC_CALL();
	
#define TEMP_LOGO_POS 1
#define TEMP_LOGO_LEN 16
#define TEMP_SUBTITLE_POS 1
#define TEMP_SUBTITLE_LEN 16
	float ratio = 0;
	float ratio1 = 0;
	float ratio2 = 0;
	long ret = 1;
	long width = 0;
	long height = 0;
	unsigned char logo_buf[TEMP_LOGO_LEN*TEMP_LOGO_LEN*4] = {0};
	CGRect logo_rect = {TEMP_LOGO_POS,TEMP_LOGO_POS,TEMP_LOGO_POS+TEMP_LOGO_LEN,TEMP_LOGO_POS+TEMP_LOGO_LEN};
	CGSize logo_size = {TEMP_LOGO_LEN,TEMP_LOGO_LEN};
	unsigned char subtitle_buf[TEMP_SUBTITLE_LEN*TEMP_SUBTITLE_LEN*4] = {0};
	CGRect subtitle_rect = {TEMP_SUBTITLE_POS,TEMP_SUBTITLE_POS,TEMP_SUBTITLE_POS+TEMP_SUBTITLE_LEN,TEMP_SUBTITLE_POS+TEMP_SUBTITLE_LEN};
	CGSize subtitle_size = {TEMP_SUBTITLE_LEN,TEMP_SUBTITLE_LEN};
	
	if (rect && src_rect)
	{		
		LOGDB(@"[TVOUT render]call set_video_rect rect(%d,%d,%d,%d),src_rect(%d,%d,%d,%d) is_first=%d",
			rect->left,rect->top,rect->right,rect->bottom,
			src_rect->left,src_rect->top,src_rect->right,src_rect->bottom);
		
		width = src_rect->size.width;
		height = src_rect->size.height;
		video_rect = *rect;
		video_src_rect = *src_rect;
		glViewport(rect->origin.x, rect->origin.y, rect->size.width, rect->size.height);
        backingWidth = rect->size.width;
        backingHeight = rect->size.height;
		ratio1 = (float)(rect->size.height) / (src_rect->size.height);
		ratio2 = (float)(rect->size.width) / (src_rect->size.width);
		if ((src_rect->size.height) * (rect->size.width) >= (src_rect->size.width) * (rect->size.height))
		{
			ratio = ratio1;
			vec_ratio = ratio1 / ratio2;
		}
		else 
		{
			ratio = ratio2;
			vec_ratio = ratio2 / ratio1;
		}
		sub_img_height = (long)((rect->size.height) / ratio);
		sub_img_width = (long)((rect->size.width) / ratio);
		texture_size_x = texture_size_y = 256;
		while (texture_size_x < (sub_img_width + 2))
		{
			texture_size_x *= 2;
		}
		while (texture_size_y < (sub_img_height + 2))
		{
			texture_size_y *= 2;
		}
		
		//if (picture_linesize_y == 0)
		//{
			picture_linesize_y = sub_img_width + 2;
		//}
		//if (picture_linesize_u == 0)
		//{
			picture_linesize_u = sub_img_width + 2;
		//}
		//if (picture_linesize_v == 0)
		//{
			picture_linesize_v = sub_img_width + 2;
		//}
		
		texture_size_x_real = 256;
		while (texture_size_x_real < picture_linesize_y)
		{
			texture_size_x_real *= 2;
		}
		
		/*if (logo_texture)
		{
			glDeleteTextures(1, &logo_texture);
			logo_texture = 0;
		}
		if (subtitle_texture)
		{
			glDeleteTextures(1, &subtitle_texture);
			subtitle_texture = 0;
		}*/
		if (spriteTexture)
		{
			glDeleteTextures(1, &spriteTexture);
			spriteTexture = 0;
		}
		end_yuv_texture(&yuv_ctx);
		
		if (sub_rgb)
		{
			ekk_free(sub_rgb);
			sub_rgb = NULL;
		}

		//if (size_mode == EKK_VIDEO_NORMAL_SIZE)
		//{
			spriteTexcoords[0] = spriteTexcoords[4] = spriteTexcoords[1] = spriteTexcoords[3] = 0;
			spriteTexcoords[2] = spriteTexcoords[6] = sprite_tex_x;
			spriteTexcoords[5] = spriteTexcoords[7] = sprite_tex_y;
		//}
		//else if (size_mode == EKK_VIDEO_LARGE_SIZE)
		//{
		//	spriteTexcoords[0] = spriteTexcoords[4] = (sprite_tex_x - sprite_tex_x * vec_ratio) / 2;
		//	spriteTexcoords[1] = spriteTexcoords[3] = (sprite_tex_y - sprite_tex_y * vec_ratio) / 2;
		//	spriteTexcoords[2] = spriteTexcoords[6] = (sprite_tex_x + sprite_tex_x * vec_ratio) / 2;
		//	spriteTexcoords[5] = spriteTexcoords[7] = (sprite_tex_y + sprite_tex_y * vec_ratio) / 2;
		//}
		
		sprite_tex_x = (float)sub_img_width / texture_size_x;
		sprite_tex_y = (float)sub_img_height / texture_size_y;
		
		g_magnify_ratio = ratio;
		
		//When width is not times of 8, we should use rgba back of avoiding single byte texture problem.
#if 1
		EKK_VIDEO_FRAME_TYPE vft = EKK_NONE;
		ekk_get_video_frame_type(&vft);
		if (vft != EKK_YUV420 && vft != EKK_Y4UV4)
		{
			yuv_ctx.use_yuv = false;
		}
		else
		{
			yuv_ctx.use_yuv = true;
		}
#endif
		
		if (program)
		{
			glDeleteProgram(program);
			program = 0;
		}
		[self loadShaders];
        GLuint default_program =program;
        GLint *default_uniforms;
        default_uniforms=uniforms;
        for(int i=0;i <4;i++)
        {
            program =program_array[i];
            uniforms=uniforms_array[i];
		glUseProgram(program);
		glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, spriteVertices);
		glEnableVertexAttribArray(ATTRIB_VERTEX);
		glVertexAttribPointer(ATTRIB_COORD, 2, GL_FLOAT, 0, 0, spriteTexcoords);
		glEnableVertexAttribArray(ATTRIB_COORD);
		glVertexAttribPointer(ATTRIB_LOGOCOORD, 2, GL_FLOAT, 0, 0, logoTexcoords);
		glEnableVertexAttribArray(ATTRIB_LOGOCOORD);
		glVertexAttribPointer(ATTRIB_SUBTITLECOORD, 2, GL_FLOAT, 0, 0, subtitleTexcoords);
		glEnableVertexAttribArray(ATTRIB_SUBTITLECOORD);
		//	glUniform1i(uniforms[UNIFORM_TEXTURE], spriteTexture);	
		glUniform1i(uniforms[UNIFORM_TEXTURE], 0);	
		glUniform1i(uniforms[UNIFORM_LOGO_TEXTURE], 1);	
		glUniform1i(uniforms[UNIFORM_SUBTITLE_TEXTURE], 2);	
		glUniform1i(uniforms[UNIFORM_USELOGO], 0);
		
		glEnableVertexAttribArray(0);
		}
        uniforms=default_uniforms;
        program=default_program;
		if(yuv_ctx.use_yuv)
		{
			prepare_yuv_texture(&yuv_ctx, program, texture_size_x_real, texture_size_y, GL_TEXTURE3,program_array);
			spriteTexcoords[2] = spriteTexcoords[6] = sprite_tex_x;
			spriteTexcoords[5] = spriteTexcoords[7] = sprite_tex_y;
			
			/*	don't stretch the video
			spriteVertices[0]=spriteVertices[4]=(float)-1*(float)sub_img_width/(rect->right - rect->left);
			spriteVertices[5]=spriteVertices[7]= (float)-1*(float)sub_img_height/(rect->bottom - rect->top);
			spriteVertices[2]=spriteVertices[6]=(float)sub_img_width/(rect->right - rect->left);
			spriteVertices[1]=spriteVertices[3]= (float)sub_img_height/(rect->bottom - rect->top);
			*/
			
			glUseProgram(program);
			
			//glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
			
			ret = 0;
			
			//glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
			//[context presentRenderbuffer:GL_RENDERBUFFER];
		}
		else
		{
			/*	don't stretch the video 
			 spriteVertices[0]=spriteVertices[4]=(float)-1*(float)sub_img_width/(rect->right - rect->left);
			 spriteVertices[5]=spriteVertices[7]= (float)-1*(float)sub_img_height/(rect->bottom - rect->top);
			 spriteVertices[2]=spriteVertices[6]=(float)sub_img_width/(rect->right - rect->left);
			 spriteVertices[1]=spriteVertices[3]= (float)sub_img_height/(rect->bottom - rect->top);
			 */
			glGenTextures(1, &spriteTexture);
			// Bind the texture name. 
			/*		
			 glBindTexture(GL_TEXTURE_2D, spriteTexture);
			 
			 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
			 
			 glUniform1i(uniforms[UNIFORM_TEXTURE], spriteTexture);	
			 */
			glActiveTexture(GL_TEXTURE0);
			//		glClearColor(0.0f, 0.0f, 0.0f, 1.0f);   
			glBindTexture(GL_TEXTURE_2D, spriteTexture);
			
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
			
			// Enable use of the texture
			
			glEnable(GL_TEXTURE_2D);
			sub_rgb = ekk_malloc(texture_size_x_real * texture_size_y * 4);
			if (sub_rgb)
			{
				ekk_memset(sub_rgb, 0, texture_size_x_real * texture_size_y * 4);
				glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture_size_x_real, texture_size_y, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char*)sub_rgb);			
				ekk_free(sub_rgb);
				sub_rgb = NULL;
				spriteTexcoords[2] = spriteTexcoords[6] = sprite_tex_x;
				spriteTexcoords[5] = spriteTexcoords[7] = sprite_tex_y;
//				glClear(GL_COLOR_BUFFER_BIT);
				// Use shader program
				glUseProgram(program);
				
				//glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
				
				//glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
				//[context presentRenderbuffer:GL_RENDERBUFFER];
			}
		}
		
		//Prepare Logo buffer to avoid error.
		if (logo_texture == 0)
		{
			[self set_video_logo:&logo_rect logo_buffer_name:logo_buf logo_size_name:&logo_size];
		}
		if (subtitle_texture == 0)
		{
			[self set_video_subtitle:&subtitle_rect subtitle_buffer_name:subtitle_buf subtitle_size_name:&subtitle_size];
		}
	}
	
	if (size_mode == EKK_VIDEO_NORMAL_SIZE)
	{
		spriteTexcoords[0] = spriteTexcoords[4] = spriteTexcoords[1] = spriteTexcoords[3] = 0;
		spriteTexcoords[2] = spriteTexcoords[6] = sprite_tex_x;
		spriteTexcoords[5] = spriteTexcoords[7] = sprite_tex_y;
	}
	else if (size_mode == EKK_VIDEO_LARGE_SIZE)
	{
		spriteTexcoords[0] = spriteTexcoords[4] = (sprite_tex_x - sprite_tex_x * vec_ratio) / 2;
		spriteTexcoords[1] = spriteTexcoords[3] = (sprite_tex_y - sprite_tex_y * vec_ratio) / 2;
		spriteTexcoords[2] = spriteTexcoords[6] = (sprite_tex_x + sprite_tex_x * vec_ratio) / 2;
		spriteTexcoords[5] = spriteTexcoords[7] = (sprite_tex_y + sprite_tex_y * vec_ratio) / 2;
	}
	else if (size_mode == EKK_VIDEO_ORIGINAL_SIZE)
	{
		spriteTexcoords[0] = spriteTexcoords[4] = (sprite_tex_x - sprite_tex_x * g_magnify_ratio) / 2;
		spriteTexcoords[1] = spriteTexcoords[3] = (sprite_tex_y - sprite_tex_y * g_magnify_ratio) / 2;
		spriteTexcoords[2] = spriteTexcoords[6] = (sprite_tex_x + sprite_tex_x * g_magnify_ratio) / 2;
		spriteTexcoords[5] = spriteTexcoords[7] = (sprite_tex_y + sprite_tex_y * g_magnify_ratio) / 2;
	}
    
	return ret;
}

- (long)set_video_logo:(CGRect*)logo_rect logo_buffer_name:(unsigned char *)logo_buffer logo_size_name:(CGSize*)logo_size
{
	LOG_FUNC_CALL();

	
	LOGDB(@"[TVOUT %d] render set_video_logo (%p %p %p)",self,logo_rect,logo_buffer,logo_size);	
	
	long ret = 1;
	long width = 0;
	long height = 0;
	long video_width = 0;
	long video_height = 0;
	long logo_texture_x = 0;
	long logo_texture_y = 0;
	if (NULL == logo_buffer || NULL == logo_rect || NULL == logo_size)
	{
		/*if (logo_texture)
		{
			glDeleteTextures(1, &logo_texture);
			logo_texture = 0;
		}
		glUniform1i(uniforms[UNIFORM_USELOGO], 0);*/
		ret = 0;
	}
	else
	{
	
	LOGDB(@"[TVOUT %d] render set_video_logo (%d %d %d %d)(%p)(%d %d) ",self,\
		logo_rect->left,logo_rect->top,logo_rect->right,logo_rect->bottom,\
		logo_buffer,logo_size->x,logo_size->y);	
	
		video_logo_rect = *logo_rect;
		video_logo_buffer = logo_buffer;
		video_logo_size.width = logo_size->width;
		video_logo_size.height = logo_size->height;
	
		width = logo_rect->size.width;
		height = logo_rect->size.height;
		video_width = video_rect.size.width;
		video_height = video_rect.size.height;
		
		logo_texture_x = logo_texture_y = 16;
		while (logo_texture_x < logo_size->width || logo_texture_x * 2 < video_width)
		{
			logo_texture_x *= 2;
		}
		while (logo_texture_y < logo_size->height || logo_texture_y * 2 < video_height)
		{
			logo_texture_y *= 2;
		}
		if (logo_texture)
		{
			glDeleteTextures(1, &logo_texture);	
			logo_texture = 0;
		}
		if (sub_rgb)
		{
			ekk_free(sub_rgb);
			sub_rgb = NULL;
		}
		glGenTextures(1, &logo_texture);
		glActiveTexture(GL_TEXTURE1);
		glBindTexture(GL_TEXTURE_2D, logo_texture);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		glEnable(GL_TEXTURE_2D);
		sub_rgb = ekk_malloc(logo_texture_x * logo_texture_y * 4);
		if (sub_rgb)
		{
			ekk_memset(sub_rgb, 0, logo_texture_x * logo_texture_y * 4);
			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, logo_texture_x, logo_texture_y, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char*)sub_rgb);
			ekk_free(sub_rgb);
			sub_rgb = NULL;
			glTexSubImage2D(GL_TEXTURE_2D, 0, 1, 1, logo_size->width, logo_size->height,GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char*)logo_buffer);
			if (logo_rect->origin.x > 0)
			{
				logoTexcoords[0] = logoTexcoords[4] = - (float)width * logo_rect->origin.x / (logo_texture_x * logo_size->width);
			}
			else 
			{
				logoTexcoords[0] = logoTexcoords[4] = 0;
			}
			if (logo_rect->origin.y > 0)
			{
				logoTexcoords[1] = logoTexcoords[3] = - (float)height * logo_rect->origin.y / (logo_texture_y * logo_size->height);
			}
			else
			{
				logoTexcoords[1] = logoTexcoords[3] = 0;
			}
			logoTexcoords[2] = logoTexcoords[6] = (float)width * (video_width - logo_rect->origin.x) / (logo_texture_x * logo_size->width);
			logoTexcoords[5] = logoTexcoords[7] = (float)height * (video_height - logo_rect->origin.y) / (logo_texture_y * logo_size->height);
			glUniform1i(uniforms[UNIFORM_USELOGO], 1);
			
		}
		glUseProgram(program);

		LOGDB(@"[TVOUT %d] render set_video_logo logoTexcoords(%f %f %f %f %f %f %f %f)",self,\
		logoTexcoords[0],logoTexcoords[1],logoTexcoords[2],logoTexcoords[3],\
		logoTexcoords[4],logoTexcoords[5],logoTexcoords[6],logoTexcoords[7]);	
	}
	return ret;
}

- (long)set_video_subtitle:(CGRect*)subtitle_rect subtitle_buffer_name:(unsigned char *)subtitle_buffer subtitle_size_name:(CGSize*)subtitle_size
{
	long ret = 1;
	long width = 0;
	long height = 0;
	long video_width = 0;
	long video_height = 0;
	long subtitle_texture_x = 0;
	long subtitle_texture_y = 0;
	unsigned char *pbuf = NULL;
	if (NULL == subtitle_buffer || NULL == subtitle_rect || NULL == subtitle_size)
	{
		/*if (subtitle_texture)
		{
			glDeleteTextures(1, &subtitle_texture);
			subtitle_texture = 0;
		}*/
		ret = 0;
	}
	else
	{
		width = subtitle_rect->size.width;
		height = subtitle_rect->size.height;
		video_width = video_rect.size.width;
		video_height = video_rect.size.height;
		
		subtitle_texture_x = subtitle_texture_y = 16;
		while (subtitle_texture_x < subtitle_size->width || subtitle_texture_x * 2 < video_width)
		{
			subtitle_texture_x *= 2;
		}
		while (subtitle_texture_y < subtitle_size->height || subtitle_texture_y * 2 < video_height)
		{
			subtitle_texture_y *= 2;
		}
		if (subtitle_texture)
		{
			glDeleteTextures(1, &subtitle_texture);	
			subtitle_texture = 0;
		}
		
		glGenTextures(1, &subtitle_texture);
		glActiveTexture(GL_TEXTURE2);
		glBindTexture(GL_TEXTURE_2D, subtitle_texture);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		glEnable(GL_TEXTURE_2D);
		pbuf = ekk_malloc(subtitle_texture_x * subtitle_texture_y * 4);
		if (pbuf)
		{
			ekk_memset(pbuf, 0, subtitle_texture_x * subtitle_texture_y * 4);
			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, subtitle_texture_x, subtitle_texture_y, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char*)pbuf);
			ekk_free(pbuf);
			
			glTexSubImage2D(GL_TEXTURE_2D, 0, 1, 1, subtitle_size->width, subtitle_size->height,GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char*)subtitle_buffer);
			
			subtitle_width = subtitle_size->width;
			subtitle_height = subtitle_size->height;
			
			if (subtitle_rect->origin.x > 0)
			{
				subtitleTexcoords[0] = subtitleTexcoords[4] = - (float)width * subtitle_rect->origin.x / (subtitle_texture_x * subtitle_size->width);
			}
			else 
			{
				subtitleTexcoords[0] = subtitleTexcoords[4] = 0;
			}
			if (subtitle_rect->origin.y > 0)
			{
				subtitleTexcoords[1] = subtitleTexcoords[3] = - (float)height * subtitle_rect->origin.y / (subtitle_texture_y * subtitle_size->height);
			}
			else
			{
				subtitleTexcoords[1] = subtitleTexcoords[3] = 0;
			}
			subtitleTexcoords[2] = subtitleTexcoords[6] = (float)width * (video_width - subtitle_rect->origin.x) / (subtitle_texture_x * subtitle_size->width);
			subtitleTexcoords[5] = subtitleTexcoords[7] = (float)height * (video_height - subtitle_rect->origin.y) / (subtitle_texture_y * subtitle_size->height);
			
		}
		glUseProgram(program);
	}
	return ret;
}
- (long)open_video_subtitle
{
    
    if(!yuv_ctx.use_yuv)
        return 0;
    if((program == program_array[0] )||(program == program_array[1]))
    {
        program = program_array[1];
        uniforms = uniforms_array[1];
    }else
    {
        program = program_array[3];
        uniforms = uniforms_array[3];
    }
}
- (long)close_video_subtitle
{
    
    if(!yuv_ctx.use_yuv)
        return 0;
    if((program == program_array[0]) ||(program == program_array[1] ))
    {
        program =program_array[0];
        uniforms=uniforms_array[0];
    }else
    {
        program =program_array[2];
        uniforms=uniforms_array[2];
    }
    return 0;
}
- (long)set_video_ps:(long)bri contrast_name:(long)con saturation_name:(long)sat
{
    
	
    
    if(!yuv_ctx.use_yuv)
        return 0;
    
    float brightness=bri/100.0;
    float contrast=con/100.0;
    float saturation= sat/100.0;
    
    if((brightness == default_brightness) && (contrast==default_contrast) && (saturation == default_saturation))
        return 0;
    //printf("brightness=%f,contrast=%f,saturation=%f",brightness,contrast,saturation);
    if((brightness==1.0 )&&(contrast==1.0)&&(saturation==1.0))
    {
        ps_changed =0;
    	if((program == program_array[0]) ||(program == program_array[1]))
			return 0;
        if((program == program_array[0] )||(program == program_array[2]))
        {
            program = program_array[0];
            uniforms = uniforms_array[0];
        }else
        {
            program = program_array[1];
            uniforms = uniforms_array[1];
        }
        return 0;
    }   
    default_contrast=contrast;
    default_brightness=brightness;
    default_saturation=saturation;
    
    if(brightness < 0.0)
        brightness=1.0;
    if(contrast < 0.0)
        contrast=1.0;
    if(saturation < 0.0)
        saturation=1.0;
    if(brightness > 2.0)
        brightness=1.0;
    if(contrast >2.0 )
        contrast=1.0;
    if(saturation > 2.0)
        saturation=1.0;
	brightness -= 1.0;
    
    GLfloat y1,y2,y3,u1,u2,u3,v1,v2,v3,w1,w2,w3;
    GLfloat satcon =2.0*saturation*contrast;
    GLfloat jiansatcon=(1.0-saturation)*contrast;
    GLfloat bricon =brightness-contrast+0.5;
    GLfloat ycon=jiansatcon*2.3280;
    GLfloat ucon=jiansatcon*(-0.2684);
    GLfloat vcon=jiansatcon*(-0.4849);
    GLfloat wcon=jiansatcon*0.2312;
    y1=ycon+satcon*1.164;
    u1=ucon;
    v1=vcon+satcon*1.596;
    w1=wcon+bricon+satcon*(-0.8708);
    y2=y1;
    u2=ucon+satcon*(-0.391);
    v2=vcon+satcon*(-0.813);
    w2=wcon+bricon +satcon*0.5293;
    y3=y1;
    u3=ucon+satcon*2.018;
    v3=vcon;
    w3=wcon+bricon+satcon*(-1.0818);
    
    /*GLfloat sat2=2*saturation;
    GLfloat satjian=1-saturation;
    GLfloat xh= contrast*(0.4250*satjian+sat2);
    GLfloat xl= contrast*(0.4250*satjian);
    GLfloat yh= contrast*(1.4308*satjian+sat2);
    GLfloat yl= contrast*(1.4308*satjian);
    GLfloat zh= contrast*(0.1442*satjian+sat2);
    GLfloat zl= contrast*(0.1442*satjian);
    GLfloat con =0.5-contrast+brightness;*/
    
    //pathindex=glGetUniformLocation(program,"a1");
    //glUniform1f(pathindex, a1);
    //pathindex=glGetUniformLocation(program,"a2");
    //glUniform1f(pathindex, a2);
    //pathindex=glGetUniformLocation(program,"a3");
    //glUniform1f(pathindex, a3);
    //pathindex=glGetUniformLocation(program,"avec");
    //glUniform3fv(pathindex,3, avec);
   
    
    //glUseProgram(program_array[2]);
    GLint pathindex;
    //GLfloat mask[16]={xh,yl,zl,con,xl,yh,zl,con,xl,yl,zh,con,0.0,0.0,0.0,1.0};
    //pathindex=glGetUniformLocation(program,"blendtest");
    //glUniformMatrix4fv(pathindex, 1,GL_FALSE, mask);
    
         
    const float mask_2[16]={y1,u1,v1,w1,y2,u2,v2,w2,y3,u3,v3,w3,0.0,0.0,0.0,1.0};
    ekk_memcpy(mask, mask_2, sizeof(float)*16);
    
    //pathindex=glGetUniformLocation(program_array[2],"blendtest");
    //glUniformMatrix4fv(pathindex, 1,GL_FALSE, mask);
	
	//glUseProgram(program_array[3]);
	//pathindex=glGetUniformLocation(program_array[3],"blendtest");
    //glUniformMatrix4fv(pathindex, 1,GL_FALSE, mask);
	ps_changed =100;
    
	
	
    
    return 0;
}
- (void)dealloc
{
	LOG_FUNC_CALL();

    if (lock)
    {
        ekk_mutex_destroy((EKK_MUTEX_HANDLE)lock);
        lock = NULL;
    }
    
	if (spriteTexture)
	{
		glDeleteTextures(1, &spriteTexture);
		spriteTexture = 0;
	}
	
	if (logo_texture)
	{
		glDeleteTextures(1, &logo_texture);
		logo_texture = 0;
	}
	
	if (subtitle_texture)
	{
		glDeleteTextures(1, &subtitle_texture);
		subtitle_texture = 0;
	}
	
	if (sub_rgb)
	{
		ekk_free(sub_rgb);
		sub_rgb = NULL;
	}

    if (program)
    {
        glDeleteProgram(program);
        program = 0;
    }
	
	if(yuv_ctx.use_yuv)
	{
		end_yuv_texture(&yuv_ctx);
	}

    // Tear down context
    if ([EAGLContext currentContext] == context)
        [EAGLContext setCurrentContext:nil];

    [context release];
    context = nil;
    

    [super dealloc];
}

- (long)set_size_mode:(long)size_mode_value
{
	if (size_mode_value != size_mode)
	{
		size_mode = size_mode_value;
		if (size_mode == EKK_VIDEO_NORMAL_SIZE)
		{
			spriteTexcoords[0] = spriteTexcoords[4] = spriteTexcoords[1] = spriteTexcoords[3] = 0;
			spriteTexcoords[2] = spriteTexcoords[6] = sprite_tex_x;
			spriteTexcoords[5] = spriteTexcoords[7] = sprite_tex_y;
		}
		else if (size_mode == EKK_VIDEO_LARGE_SIZE)
		{
			spriteTexcoords[0] = spriteTexcoords[4] = (sprite_tex_x - sprite_tex_x * vec_ratio) / 2;
			spriteTexcoords[1] = spriteTexcoords[3] = (sprite_tex_y - sprite_tex_y * vec_ratio) / 2;
			spriteTexcoords[2] = spriteTexcoords[6] = (sprite_tex_x + sprite_tex_x * vec_ratio) / 2;
			spriteTexcoords[5] = spriteTexcoords[7] = (sprite_tex_y + sprite_tex_y * vec_ratio) / 2;
		}
		else if (size_mode == EKK_VIDEO_ORIGINAL_SIZE)
		{
			spriteTexcoords[0] = spriteTexcoords[4] = (sprite_tex_x - sprite_tex_x * g_magnify_ratio) / 2;
			spriteTexcoords[1] = spriteTexcoords[3] = (sprite_tex_y - sprite_tex_y * g_magnify_ratio) / 2;
			spriteTexcoords[2] = spriteTexcoords[6] = (sprite_tex_x + sprite_tex_x * g_magnify_ratio) / 2;
			spriteTexcoords[5] = spriteTexcoords[7] = (sprite_tex_y + sprite_tex_y * g_magnify_ratio) / 2;
		}
	}
	return 0;
}

- (long)refresh_video
{
    ekk_mutex_lock((EKK_MUTEX_HANDLE)lock);
	glUseProgram(program);
			
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
			
	glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
	[context presentRenderbuffer:GL_RENDERBUFFER];
    ekk_mutex_unlock((EKK_MUTEX_HANDLE)lock);
	return 0;
}

- (long)get_size_mode
{
	return size_mode;
}

@end

